#pragma once

#include <nanobdd/common.h>
#include <nanobdd/node.h>
#include <boost/unordered_map.hpp>
#include <list>
#include <map>
#include <optional>
#include <tuple>
#include <vector>

namespace nanobdd {

class NodeCache {
 public:
  NodeCache() = default;
  NodeCache(size_t size) : size_(size) {
    // cache_.resize(size);
  }
  struct CacheEntry {
    std::shared_ptr<Node> node;
    uint32_t level;
    std::shared_ptr<Node> low;
    std::shared_ptr<Node> high;
  };

  void
  insert(std::shared_ptr<Node> node) {
    auto key = boost::multiprecision::uint256_t(node->level()) << 128 |
        boost::multiprecision::uint256_t((uint64_t)node->low().get()) << 64 |
        boost::multiprecision::uint256_t((uint64_t)node->high().get());
    cache_[key] = node;
    // auto hash =
    //     TRIPLEp(node->level(), node->low().get(), node->high().get()) %
    //     size_;
    // auto& entry = cache_.at(hash);
    // entry.node = node;
    // entry.level = node->level();
    // entry.low = node->low();
    // entry.high = node->high();
  }

  std::optional<std::shared_ptr<Node>>
  lookup(
      uint32_t level, std::shared_ptr<Node> low, std::shared_ptr<Node> high) {
    auto key = boost::multiprecision::uint256_t(level) << 128 |
        boost::multiprecision::uint256_t((uint64_t)low.get()) << 64 |
        boost::multiprecision::uint256_t((uint64_t)high.get());
    auto iter = cache_.find(key);
    if (iter != cache_.end()) {
      return iter->second;
    }
    return std::nullopt;
    // auto hash = TRIPLEp(level, low.get(), high.get()) % size_;

    // auto& entry = cache_.at(hash);
    // if (entry.level == level && entry.low == low && entry.high == high) {
    //   return entry.node;
    // }

    // return std::nullopt;
  }

  void
  resize(size_t size) {
    size_ = size;
    // cache_.resize(size);
  }

  size_t
  size() {
    return cache_.size();
  }

  auto&
  cache() {
    return cache_;
  }

 private:
  uint32_t size_;
  // std::vector<std::list<CacheEntry>> cache_;
  boost::unordered_map<boost::multiprecision::uint256_t, std::shared_ptr<Node>> cache_;
};

} // namespace nanobdd